%%%-------------------------------------------------------------------
%%% @author Administrator
%%% @copyright (C) 2019, <COMPANY>
%%% @doc
%%%通比牛牛
%%% @end
%%% Created : 31. 12月 2019 11:22
%%%-------------------------------------------------------------------
-module(game_tbniuniu).
-author("Administrator").
-include("../include/game.hrl").
-include("../include/player.hrl").
-include("../include/protocol.hrl").
-include("../include/logger.hrl").
-behaviour(gen_server).

%% API
-export([start_link/1, checkout_request/0]).

%% gen_server callbacks
-export([init/1,
  handle_call/3,
  handle_cast/2,
  handle_info/2,
  terminate/2,
  code_change/3]).

-define(SERVER, ?MODULE).

-record(state, {}).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @end
%%--------------------------------------------------------------------
-spec(start_link(Arg :: list()) ->
  {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link(Arg) ->
  gen_server:start_link(?MODULE, Arg, []).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
-spec(init(Args :: term()) ->
  {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
  {stop, Reason :: term()} | ignore).
init(PlayerRecord = #player_state{game_id = GameId, room_id = RoomId, account = Account, id = PlayerId,
  token = Token, room_type = RoomType, task_id = Task_id}) ->
  ?INFO("Init game app:~p~n", [PlayerRecord]),
  {ok, WsPid} = init_webSocket(GameId, Token, "0"),
  erlang:put(?GAME_WS_CONN_PID, WsPid),
  PlayerInfo = player_assist:get_player_info(Account),
  LeftScore = PlayerInfo#player_info.score,%身上余额
  NewState = #room_status{game_id = GameId, room_id = RoomId, account = Account, player_id = PlayerId,
    token = Token, ws_conn_pid = WsPid, left_score = LeftScore,
    profit_threshold = PlayerInfo#player_info.condition, room_type = RoomType,
    group_id = PlayerInfo#player_info.group_id, task_id = Task_id, is_look_bet = PlayerRecord#player_state.is_look_bet},
  ?INFO("Enter the game room :playerID:~p;game_id:~p;roomId:~p", [PlayerId, GameId, RoomId]),
  PlayerPID = list_to_atom("pid_" ++ Account),
  erlang:put(?GAME_ROOM_INFO, NewState),
  erlang:put(?TAKE_NUMBER, 0),
  erlang:send_after(2000, PlayerPID, {into_game_room}),
  Pid = list_to_atom("pid_" ++ Account ++ "_" ++ integer_to_list(GameId)),
  register(Pid, self()),
  %定时器
  erlang:send_after(5000 * 60, self(), {timer_ten_min}),
  {ok, NewState}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
    State :: #state{}) ->
  {reply, Reply :: term(), NewState :: #state{}} |
  {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
  {noreply, NewState :: #state{}} |
  {noreply, NewState :: #state{}, timeout() | hibernate} |
  {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
  {stop, Reason :: term(), NewState :: #state{}}).
%%获取游戏房间状态
handle_call(get_state, _From, State) ->

  {reply, State, State};

handle_call({stop}, _From, State) ->
  case State#room_status.player_state of
    ?LOBBY -> ok;
    ?LOOK -> out_room();%离开房间
    _ -> checkout()%结算离开
  end,
  {stop, normal, noreply, State};

handle_call(_Request, _From, State) ->
  {reply, ok, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @end
%%--------------------------------------------------------------------
-spec(handle_cast(Request :: term(), State :: #state{}) ->
  {noreply, NewState :: #state{}} |
  {noreply, NewState :: #state{}, timeout() | hibernate} |
  {stop, Reason :: term(), NewState :: #state{}}).

handle_cast({stop}, State) ->
  ?DEBUG("handle_cast STOP account:~p", [State#room_status.account]),
  %结算离开
  self() ! {checkout},
  {stop, normal, State};
handle_cast(_Request, State) ->
  {noreply, State}.


%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
  {noreply, NewState :: #state{}} |
  {noreply, NewState :: #state{}, timeout() | hibernate} |
  {stop, Reason :: term(), NewState :: #state{}}).
%%更新游戏房间状态数据
handle_info({update_state, NewState}, _State) ->
  ?DEBUG("game update state:~p", [NewState]),
  erlang:put(?GAME_ROOM_INFO, NewState),
  {noreply, NewState};

%%庄家任务完成
handle_info({task_complete}, State) ->
  erlang:put(?ZJ_TASK_COMPLETE, true),
  {noreply, State};

handle_info({timer_ten_min}, State) ->
  case lists:member(State#room_status.player_state, [?LOOK, ?LOBBY]) of
    true ->
      case player_assist:get_first_room(State#room_status.account) of
        [] -> ok;
        _ ->
          ?INFO("The player stands up and sits down again account:~p", [State#room_status.account]),
          self() ! {room_index_info}
      end;
    _ -> ok
  end,
  erlang:send_after(1000 * 60, self(), {timer_ten_min}),
  {noreply, State};

%%进入房间
handle_info({into_game_room, RoomId}, State) ->
  into_game_room(RoomId),
  ?INFO("handle_info into_game_room roomid:~p", [RoomId]),
  NewState = State#room_status{room_id = RoomId, total_plate = 0, into_integral = 0, room_score = 0},
  erlang:put(?GAME_ROOM_INFO, NewState),
  {noreply, NewState};


handle_info({gun_ws, FormId, _Ref, Msg}, State) ->
  case Msg of
    {text, MsgData} ->
      %逻辑消息处理
      msg_tbniuniu:message_response(FormId, MsgData, State),
      {noreply, State};
    {close, _, _} ->
      ?ERROR("close ws connection account:~p", [State#room_status.account]),
      {noreply, State};
    _ ->
      {ok, WsPid} = init_webSocket(State#room_status.game_id, State#room_status.token, "1"),
      NewState = State#room_status{ws_conn_pid = WsPid},
      {noreply, NewState}
  end;

handle_info({gun_down, FormId, _Protocol, _Reason, _Killed, _Unprocessed}, State = #room_status{ws_conn_pid = Conn_pid}) when FormId =:= Conn_pid ->
  ?ERROR("game gun_down Connection lost, reconnect,formid:~p,_Reason:~p", [FormId, _Reason]),
  self() ! {reconnect},
  {noreply, State};

handle_info({gun_error, FormId, _Reason}, State) ->
  ?ERROR("game gun_error Connection lost, reconnect,formid:~p,reason:~p,account:~p", [FormId, _Reason, State#room_status.account]),
  self() ! {reconnect},
  {noreply, State};

handle_info({gun_error, FormId, _StreamRef, _Reason},
    State = #room_status{ws_conn_pid = ConnPid}) when FormId =:= ConnPid ->
  ?ERROR("game other gun_errror reason:~p;_StreamRef:~p,account:~p", [_Reason, _StreamRef, State#room_status.account]),
  self() ! {reconnect},
  {noreply, State};


%%重新连接
handle_info({reconnect}, State) ->
  ?INFO("websocket reconnect account:~p", [State#room_status.account]),
  {ok, Ws_pid} = init_webSocket(State#room_status.game_id, State#room_status.token, "1"),
  {noreply, State#room_status{ws_conn_pid = Ws_pid}};

handle_info({Ref, badarg}, State) when is_reference(Ref) ->
  ?ERROR("game badarg ref:~p,stateName:~p",[Ref,State]),
  {noreply, State};

handle_info(Info, State) ->
  ?INFO("tongbi niuniu receive msg ~p~n", [Info]),
  try
    NewSate2 =
      case Info of
        {room_index_info} ->
          room_index_info(State#room_status.room_id),
          State;
        {join_table} ->
          Index = State#room_status.index,
          join_table(Index),
          State;
        %指定座位入桌，排队使用
        {join_table, Index2} ->
          join_table(Index2),
          State#room_status{index = Index2, is_queue = true};
        %旁观
        {stand_look} ->
          stand_look(),
          State;
        %旁观下注
        {look_bet} ->
          look_bet(),
          State;
        %退出房间
        {out_room} ->
          out_room(),
          State;
        %押注
        {player_bet, Type, Money} ->
          ?DEBUG("player_bet:account:~p;index:~p;type:~p;money:~p", [State#room_status.account, State#room_status.index, Type, Money]),
          player_bet(State#room_status.index, Type, Money),
          State;
        %旁观下注
        {look_player_bet, Index, Type, Money} ->
          ?DEBUG("look_player_bet:account:~p;index:~p;type:~p;money:~p", [State#room_status.account, Index, Type, Money]),
          player_bet(Index, Type, Money),
          State;
        %常用语
        {daily_msg} ->
          daily_msg(),
          State;
        %表情
        {emoji_msg} ->
          emoji_msg(),
          State;
        %表情互动
        {interact_face} ->
          interact_face(),
          State;
        %亮牌
        {booking} ->
          ?DEBUG("player booking account:~p", [State#room_status.account]),
          booking(),
          State;
        %结算离开
        {checkout} ->
          case State#room_status.player_state of
            ?LOBBY -> ok;
            ?LOOK -> out_room();%离开房间
            _ ->
              %结算离开
              case State#room_status.game_id =:= ?WEI_NIU andalso State#room_status.wn_select =:= true of
                true ->
                  ?DEBUG("weiniu zhangjia checkout account:~p", [State#room_status.account]),
                  erlang:put(?IS_ZJ, true),
                  stand_look();
                _ -> checkout()
              end
          end,
          State#room_status{is_integral = false, wn_select = false};
        %带入积分
        {into_integral} ->
          Score = game_assist:get_Into_integral(State),
          GroupId = State#room_status.group_id,
          into_integral(Score, GroupId),
          State;
        {into_integral, Num} ->
          ?DEBUG("new into_integral num:~p", [Num]),
          GroupId = State#room_status.group_id,
          into_integral(Num, GroupId),
          State;
        %切牌
        {cutting} ->
          cutting(),
          State;
        %个人信息
        {player_info} ->
          player_info(),
          State;
        %抢庄
        {rob_zhuang, Num} ->
          rob_zhuang(Num),
          State;
        %下注
        {rob_bottom, Num} ->
          rob_bottom(Num, State#room_status.player_id),
          State;
        {chip_dis} ->
          chip_dis(),
          State;
        %查看排队
        {queue_info} ->
          queue_info(),
          State;
        %围牛发言
        {weiniu_speak} ->
          weiniu_speak(State),
          State;
        %围牛下注
        {weiniu_bottom, GameMoney, Type, IsShoot} ->
          weiniu_bottom(GameMoney, Type, State#room_status.index, IsShoot),
          State;
        %围牛旁观下注
        {weiniu_look_bottom, GameMoney, Type, Zj_index} ->
          List2 = [0, 1, 2, 3] -- [Zj_index],
          N = sys_util:random(3),
          Index = lists:nth(N, List2),
          weiniu_bottom(GameMoney, Type, Index, false),
          State;
        %围牛过庄
        {weiniu_guozhuang} ->
          weiniu_guozhuang(),
          State#room_status{wn_select = false};
        _ -> State
      end,
    {noreply, NewSate2}
  catch
    Class:Error:Stacktrace ->
      ?ERROR("game handel_info receive msg class:~p;error:~p;trace:~p ", [Class, Error, Stacktrace]),
      {noreply, State}
  end.


%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
    State :: #state{}) -> term()).
terminate(_Reason, State) ->
  ?INFO("stop game ~p", [State]),
  %保存游戏数据 TODO
  %关闭game ws连接
  game_close(State#room_status.ws_conn_pid),
  ok.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
    Extra :: term()) ->
  {ok, NewState :: #state{}} | {error, Reason :: term()}).
code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================
-spec(init_webSocket(GameId :: integer(), Token :: string(), Link_count :: string()) -> {ok, pid()}).
init_webSocket(GameId, Token, Link_count) ->
  LINK_NAME =
    case GameId of
      ?NIU_NIU -> ?NIU_NIU_LINK_NAME;
      ?SAN_GONG -> ?SAN_GONG_LINK_NAME;
      ?NIU_NIU_MING_PAI -> ?NIU_NIU_MING_PAI_LINK_NAME;
      ?WEI_NIU -> ?WEI_NIU_LINK_NAME
    end,

  WsHost = myshare:get_env(ws_host),
  WsPort = myshare:get_env(ws_port),
  {ok, ConnPid} = gun:open(WsHost, WsPort),
  Url = "/" ++ LINK_NAME ++ "/" ++ Token ++ "/" ++ Link_count,
  ?DEBUG("game ws url:~p",[Url]),
  gun:ws_upgrade(ConnPid,Url , []),
  {ok, ConnPid}.

%通用发送消息函数
send_msg(Frame) ->
  try
    RoomInfo = erlang:get(?GAME_ROOM_INFO),
    WsPid = erlang:get(?GAME_WS_CONN_PID),
    ?INFO("tongbiniuniu game send msg account:~p msg:~p", [RoomInfo#room_status.account, Frame]),
    gun:ws_send(WsPid, Frame)
  catch
    Class:Error:Stacktrace ->
      ?ERROR("game send msg  class:~p,error:~p,stack:~p", [Class, Error, Stacktrace])
  end.

%%房间数据
room_index_info(RoomId) ->
  Text = jsx:encode(#{code => ?NIUNIU_INDEX, data => RoomId}),
  ?DEBUG("tongbiniuniu into_gamroom_index_infoe_room send msg:~p;State:~p", [Text, get(?GAME_ROOM_INFO)]),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%加入房间
-spec into_game_room(RoomId :: string()) -> none().
into_game_room(RoomId) ->
  Text = jsx:encode(#{code => ?NIUNIU_INTO_ROOM, data => RoomId}),
  ?DEBUG("tongbiniuniu into_game_room send msg:~p;State:~p", [Text, get(?GAME_ROOM_INFO)]),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%加入牌桌下注
-spec join_table(Index :: integer()) -> none().
join_table(Index) ->
  case Index of
    undefined ->
      ?ERROR("index number undefined!");
    _ ->
      Text = jsx:encode(#{code => ?NIUNIU_JOIN_TABLE, data => Index}),
      Frame = {text, binary_to_list(Text)},
      send_msg(Frame)
  end.

%%站起旁观
-spec stand_look() -> none().
stand_look() ->
  Text = jsx:encode(#{code => ?NIUNIU_STAND_LOOK}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%旁观下注
-spec look_bet() -> none().
look_bet() ->
  Text = jsx:encode(#{code => ?NIUNIU_LOOK_BET, data => 0}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%退出房间
-spec out_room() -> none().
out_room() ->
  Text = jsx:encode(#{code => ?NIUNIU_OUT_ROOM}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%玩家下注
-spec player_bet(Index :: integer(), Type :: integer(), Money :: number()) -> none().
player_bet(Index, Type, Money2) ->
  case Index of
    undefined ->
      ?ERROR("tongbiniuniu index undefined ~p", [get(?GAME_ROOM_INFO)]);
    _ ->
      Money = game_assist:integral(Money2),
      Text = jsx:encode(#{code => ?NIUNIU_PLAYER_BET, data => #{score => Money, index => Index, type => Type}}),
      Frame = {text, binary_to_list(Text)},
      send_msg(Frame)
  end.


%常用语消息1-10
-spec daily_msg() -> none().
daily_msg() ->
  Index = rand:uniform(10),
  Text = jsx:encode(#{code => ?NIUNIU_DAILY_MSG, data => Index}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%表情表情101-138
-spec emoji_msg() -> none().
emoji_msg() ->
  Index = 100 + rand:uniform(38),
  Text = jsx:encode(#{code => ?NIUNIU_DAILY_MSG, data => Index}),
  ?INFO("emoji msg :~p~n", [Text]),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%亮牌
-spec booking() -> none().
booking() ->
  Text = jsx:encode(#{code => ?NIUNIU_BOOKING}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).


%%结算离开
-spec checkout() -> none().
checkout() ->
  Text = jsx:encode(#{code => ?NIUNIU_CHECKOUT}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%结算离开请求
checkout_request() ->
  Text = jsx:encode(#{code => ?NIUNIU_CHECKOUT_REQUEST}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).


%%积分代入
into_integral(Score, GroupId) ->
  Text =
    case GroupId of
      0 -> jsx:encode(#{code => ?NIUNIU_INTO_INTEGRAL, data => #{score => Score}});
      _ -> jsx:encode(#{code => ?NIUNIU_INTO_INTEGRAL, data => #{groupId => GroupId, score => Score}})
    end,
  ?DEBUG("into_integral text:~p", [Text]),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%个人信息
player_info() ->
  Text = jsx:encode(#{code => ?NIUNIU_PLAYER_INFO}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%抢庄
rob_zhuang(RushVillage) ->
  Text = jsx:encode(#{code => ?NIUNIU_QIANG_ZHUANG, data => #{rushVillage => RushVillage}}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%闲家押注
rob_bottom(BetsMultiple, UserId) ->
  Text = jsx:encode(#{code => ?NIUNIU_XIA_ZHU, data => #{betsMultiple => BetsMultiple, userId => UserId}}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).


%%切牌
cutting() ->
  Text = jsx:encode(#{code => ?NIUNIU_CUTTING}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%筹码数据
chip_dis() ->
  Text = jsx:encode(#{code => ?NIUNIU_CHIP_DIS, groupType => 0, moneyType => 1}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%排队信息
queue_info() ->
  Text = jsx:encode(#{code => ?NIUNIU_QUEUE_INFO}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

% 表情互动1-12
interact_face() ->
  Text = jsx:encode(#{code => 10048, data => 1, playerId => 212121}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%围牛上庄
weiniu_speak(#room_status{wn_select = Wn_select}) ->
  Text = jsx:encode(#{code => ?WEINIU_SPEAK, data => #{select => true, isLianZhuang => Wn_select}}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).

%%过庄
weiniu_guozhuang() ->
  Text = jsx:encode(#{code => ?WEINIU_SPEAK, data => #{select => false, isLianZhuang => false}}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).


%%围牛下注
weiniu_bottom(GameMoney, Type, Index, IsShoot) ->

  Text = jsx:encode(#{code => ?WEINIU_BOTTOM, data => #{bestScore => GameMoney, betsType => Type, isShoot => IsShoot, index => Index}}),
  Frame = {text, binary_to_list(Text)},
  send_msg(Frame).


%%关闭连接
game_close(ConnPid) ->
  ?INFO("close game net "),
  gun:close(ConnPid).






